<template>
  <a-layout class="container">
    <a-layout-header class="toolbar">
      <a-button-group>
        <a-button type="primary" status="warning" @click="zoom(0.2)">
          <template #icon>
            <icon-zoom-in size="22" />
          </template>
        </a-button>
        <a-button type="primary" status="warning" @click="zoom(-0.2)">
          <template #icon>
            <icon-zoom-out size="22" />
          </template>
        </a-button>
        <a-button type="primary" status="warning" @click="zoomTo(1)">
          <template #icon>
            <icon-original-size size="22" />
          </template>
        </a-button>
        <a-button type="primary" @click="centerContent">
          内容居中
        </a-button>
        <a-button v-if="design" type="primary" status="success" @click="exportJson">
          导出数据
        </a-button>
      </a-button-group>
    </a-layout-header>
    <a-layout class="container">
      <a-layout-sider
        v-if="design"
        style="height: 100%; width: 350px"
      >
        <div ref="stencilRef"></div>
      </a-layout-sider>
      <a-layout-content style="width: 100%; height: 100%;">
        <div id="container"></div>
        <teleport-container />
      </a-layout-content>
    </a-layout>

    <!-- 开始节点配置 -->
    <node-start-drawer ref="nodeStartDrawerRef" @save-config="saveConfig" />
    <!-- 用户审批节点配置 -->
    <node-user-task-drawer ref="nodeUserTaskDrawerRef" @save-config="saveConfig" />
    <!-- 抄送节点配置 -->
    <node-copy-service-drawer ref="nodeCopyServiceDrawerRef" @save-config="saveConfig" />
    <!-- 网关条件配置 -->
    <node-edge-drawer ref="nodeEdgeDrawerRef" @save-config="saveConfig" />
  </a-layout>
</template>

<script lang="ts" setup>
import { ref, onMounted, defineExpose, defineProps } from 'vue';
import { cloneDeep } from 'lodash';
import { Graph, Model,CellView } from '@antv/x6';
import { register, getTeleport } from '@antv/x6-vue-shape';
import { Snapline } from '@antv/x6-plugin-snapline';
import { Stencil } from '@antv/x6-plugin-stencil';
import {
  CONNECTING_CONFIG,
  BACKGROUND_CONFIG,
  GRID_CONFIG,
  GRID_CONFIG_PREVIEW,
  STENCIL_LAYOUT_OPTIONS,
  STENCIL_GROUPS_CONFIG,
  TOOLS_NODE,
  TOOLS_EDGE
} from '@/components/bpmn-design/config';
import { NODE_START_SHAPE, NODE_START, NODE_START_DEFINE } from '@/components/bpmn-design/nodes/node-start/node-start';
import { NODE_END_SHAPE, NODE_END, NODE_END_DEFINE } from '@/components/bpmn-design/nodes/node-end/node-end';
import {
  NODE_USER_TASK_SHAPE,
  NODE_USER_TASK,
  NODE_USER_TASK_DEFINE
} from '@/components/bpmn-design/nodes/node-user-task/node-user-task';
import {
  NODE_COPY_SERVICE_SHAPE,
  NODE_COPY_SERVICE,
  NODE_COPY_SERVICE_DEFINE
} from '@/components/bpmn-design/nodes/node-copy-service/node-copy-service';
import {
  NODE_PARALLEL_GATEWAY_SHAPE,
  NODE_PARALLEL_GATEWAY,
  NODE_PARALLEL_GATEWAY_DEFINE
} from '@/components/bpmn-design/nodes/node-parallel-gateway/node-parallel-gateway';
import {
  NODE_EXCLUSIVE_GATEWAY_SHAPE,
  NODE_EXCLUSIVE_GATEWAY,
  NODE_EXCLUSIVE_GATEWAY_DEFINE
} from '@/components/bpmn-design/nodes/node-exclusive-gateway/node-exclusive-gateway';
import {
  NODE_INCLUDE_GATEWAY_SHAPE,
  NODE_INCLUDE_GATEWAY,
  NODE_INCLUDE_GATEWAY_DEFINE
} from '@/components/bpmn-design/nodes/node-include-gateway/node-include-gateway';
import NodeStartDrawer from '@/components/bpmn-design/nodes/node-start/node-start-drawer.vue';
import NodeUserTaskDrawer from '@/components/bpmn-design/nodes/node-user-task/node-user-task-drawer.vue';
import NodeCopyServiceDrawer from '@/components/bpmn-design/nodes/node-copy-service/node-copy-service-drawer.vue';
import NodeEdgeDrawer from '@/components/bpmn-design/nodes/node-edge/node-edge-drawer.vue';
import FromJSONData = Model.FromJSONData;

// 注册节点
register(NODE_START_DEFINE);
register(NODE_END_DEFINE);
register(NODE_USER_TASK_DEFINE);
register(NODE_COPY_SERVICE_DEFINE);
register(NODE_PARALLEL_GATEWAY_DEFINE);
register(NODE_EXCLUSIVE_GATEWAY_DEFINE);
register(NODE_INCLUDE_GATEWAY_DEFINE);

const props = defineProps({
  processData: {
    type: Object,
    default: () => {
      return {};
    }
  },
  design: {
    type: Boolean,
    default: () => {
      return true;
    }
  }
});

const TeleportContainer = getTeleport();

const graph = ref<Graph>();

const stencil = ref<Stencil>();

const stencilRef = ref();

const selectCell = ref();

const nodeStartDrawerRef = ref();

const nodeUserTaskDrawerRef = ref();

const nodeCopyServiceDrawerRef = ref();

const nodeEdgeDrawerRef = ref();

const saveConfig = (configData: any) => {
  if (selectCell.value.isEdge()) {
    selectCell.value.setLabels(configData.label);
  }
  selectCell.value.setData(configData);
};

const onCellClick = ({ cell }: any) => {
  selectCell.value = cell;
  const cellData = cloneDeep(cell.getData());
  if (cell.shape === NODE_START_SHAPE) {
    nodeStartDrawerRef.value.open(cellData, props.processData);
  }
  if (cell.shape === NODE_USER_TASK_SHAPE) {
    nodeUserTaskDrawerRef.value.open(cellData, props.processData, getRefuseNode(cell));
  }
  if (cell.shape === NODE_COPY_SERVICE_SHAPE) {
    nodeCopyServiceDrawerRef.value.open(cellData, props.processData);
  }
};

const onEdgeClick = ({ edge }: any) => {
  selectCell.value = edge;
  const sourceCellType = graph.value?.getCellById(edge.getSourceCell()).shape;
  let edgeData = cloneDeep(edge.getData());
  if (!edgeData) {
    edgeData = {
      label: '',
      conditionGroup: {
        relationship: false,
        groups: []
      }
    };
  }
  if (sourceCellType === NODE_INCLUDE_GATEWAY_SHAPE || sourceCellType === NODE_EXCLUSIVE_GATEWAY_SHAPE) {
    nodeEdgeDrawerRef.value.open(edgeData, props.processData);
  }
};

const onCellMouseenter = ({ cell }: any) => {
  if (cell.isNode()) {
    cell.addTools(TOOLS_NODE);
  } else {
    cell.addTools(TOOLS_EDGE);
  }
};

const onCellMouseleave = ({ cell }: any) => {
  cell.removeTools();
};

const createGraph = () => {
  const graph = new Graph({
    container: document.getElementById('container') || undefined,
    autoResize: true,
    panning: true,
    mousewheel: true,
    connecting: CONNECTING_CONFIG,
    background: BACKGROUND_CONFIG,
    grid: props.design ? GRID_CONFIG : GRID_CONFIG_PREVIEW,
    interacting: props.design
  });
  graph.use(new Snapline({
    enabled: true,
    sharp: true
  }));
  graph.centerContent();
  if (props.design) {
    // 工具
    graph.on('cell:mouseenter', onCellMouseenter);
    graph.on('cell:mouseleave', onCellMouseleave);
    // 节点右键事件
    graph.on('cell:contextmenu', onCellClick);
    graph.on('edge:contextmenu', onEdgeClick);
  }
  return graph;
};

const createStencil = () => {
  const stencil = new Stencil({
    title: '流程组件',
    target: graph.value,
    search(cell, keyword) {
      return cell.data.label.indexOf(keyword) !== -1;
    },
    placeholder: '请输入节点名称',
    notFoundText: '未找到数据',
    collapsable: true,
    stencilGraphWidth: 350,
    stencilGraphHeight: 0,
    layoutOptions: STENCIL_LAYOUT_OPTIONS,
    groups: STENCIL_GROUPS_CONFIG
  });
  if (graph.value) {
    const nodeStart = graph.value?.createNode(NODE_START);
    const nodeEnd = graph.value?.createNode(NODE_END);
    const nodeUserTask = graph.value?.createNode(NODE_USER_TASK);
    const nodeCopyService = graph.value?.createNode(NODE_COPY_SERVICE);
    const nodeParallelGateway = graph.value?.createNode(NODE_PARALLEL_GATEWAY);
    const nodeIncludeGateway = graph.value?.createNode(NODE_INCLUDE_GATEWAY);
    const nodeExclusiveGateway = graph.value?.createNode(NODE_EXCLUSIVE_GATEWAY);
    stencil.load([nodeStart, nodeEnd], 'event');
    stencil.load([nodeUserTask, nodeCopyService], 'task');
    stencil.load([nodeParallelGateway, nodeIncludeGateway, nodeExclusiveGateway], 'gateway');
    stencilRef.value.appendChild(stencil.container);
  }
  return stencil;
};

const setJson = (data: FromJSONData) => {
  if (data) {
    graph.value?.fromJSON(data);
  }
  // 延时使内容居中 nextTick 无效
  setTimeout(() => {
    graph.value?.centerContent();
  }, 100);
};

// 获取拒绝节点配置
const getRefuseNode = (cell: any) => {
  const cells = graph.value?.getCells() as any[];
  const resultCellIds: never[] = [];
  getPreNodes(cells, cell, resultCellIds);
  const resultCells: { id: string | undefined; label: any; }[] = [];
  resultCellIds.forEach(item => {
    const cellPer = graph.value?.getCellById(item);
    if (cellPer?.shape !== NODE_START_SHAPE) {
      resultCells.push({
        id: cellPer?.id,
        label: cellPer?.getData().label
      });
    }
  });
  return resultCells;
};

/**
 * 获取节点之前的节点列表
 * todo 之后加入网关多路径可以要修改
 * @param cells
 * @param currentNodes
 * @param resultCells
 */
const getPreNodes = (cells: any[], currentNodes: any, resultCells: string[]) => {
  const incomingEdges = graph.value?.getIncomingEdges(currentNodes);
  if (incomingEdges && incomingEdges.length <= 0) {
    return;
  }
  incomingEdges?.forEach(item => {
    if (resultCells.indexOf(item.getSourceCellId()) === -1) {
      resultCells.push(item.getSourceCellId());
    }
    getPreNodes(cells, item.getSourceCellId(), resultCells);
  });
};

const zoom = (value: number) => {
  if (graph.value) {
    graph.value.zoom(value);
  }
};

const zoomTo = (value: number) => {
  if (graph.value) {
    graph.value.zoomTo(value);
  }
};

const centerContent = () => {
  if (graph.value) {
    graph.value.centerContent();
  }
};

const exportJson = () => {
  console.log(graph.value?.toJSON());
  return graph.value?.toJSON();
};

defineExpose({
  setJson,
  exportJson
});

onMounted(() => {
  graph.value = createGraph();
  if (props.design) {
    stencil.value = createStencil();
  }
});
</script>

<script lang="ts">
export default {
  name: 'BpmnDesign'
};
</script>

<style scoped lang="less">
.container {
  height: 100%;
  width: 100%;
}

.toolbar {
  display: flex;
  justify-content: end;
  margin-bottom: -34px;
  z-index: 99999;
}
</style>